package ua.edu.nuos.jeetraining2012.cms.model;

import org.slf4j.Logger;
import ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity;
import ua.edu.nuos.jeetraining2012.cms.model.exception.DAOException;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Arrays;

/**
 * InstructorDAO class.
 * <p/>
 * DAO class that works with instructors table.
 * Available actions are:
 * <ul>
 * <li>Add instructor: {@link InstructorDAO#create(ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity)}</li>
 * <li>Find instructor: {@link InstructorDAO#find(ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity)}</li>
 * <li>Update instructor: {@link InstructorDAO#update(ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity)}</li>
 * <li>Delete instructor: {@link InstructorDAO#delete(ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity)}</li>
 * <li>Find instructor by id: {@link InstructorDAO#findById(long)}</li>
 * </ul>
 * All methods accepted CourseInstructorEntity.
 *
 * @author Victor N. Ganichev victor.ganichev@gmail.com
 * @version 1.0 Date: 10.05.13 Time: 22:16
 * @since 1.0
 */
@Local
@Stateless
public class InstructorDAO {

    /**
     * Define logger
     */
    final private Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());

    /**
     * Define EntityManager
     * All transactions will be controlled by Application server.
     */
    @PersistenceContext
    private EntityManager em;

    // Getters & Setters
    protected EntityManager getEm() {
        return em;
    }

    private void setEm(EntityManager em) {
        this.em = em;
    }

// Constructors

    /**
     * Default constructor.
     */
    public InstructorDAO() {
        logger.debug("InstructorDAO - start.");
    }

    /**
     * Constructor with customized EntityManager.
     * Use this constructor for testing purposes.
     *
     * @param em Instance of EntityManager.
     */
    public InstructorDAO(EntityManager em) {
        logger.debug("InstructorDAO start with EntityManager {}", em);
        setEm(em);
    }

    /**
     * Create.
     * <p/>
     * Add new instructor into DB. In case of success, method will return an ID of just inserted record.
     * In other cases, when create command can't complete with success, {@link DAOException} will be
     * thrown and some useful information will be send into logger.
     *
     * @param dto Instance of CourseInstructorEntity.
     * @return Long id of just inserted record.
     * @throws IllegalArgumentException Will thrown in case, when dto in null.
     * @throws DAOException             Will thrown if parent Exception occurred.
     */
    public Long create(CourseInstructorEntity dto) throws IllegalArgumentException, DAOException {
        logger.debug("create() - started.");
        if (dto == null) {
            logger.warn("Null dto in create()");
            logger.debug("create() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Can't persist with null valued Entity.");
        }

        Long lastInsertId = null;
        logger.debug("Adding instructor with values {}", dto);
        try {
            em.persist(dto);
            em.flush();
            lastInsertId = dto.getId();
            logger.debug("Instructor successful added. New instructor id: {}", lastInsertId);
        } catch (Exception e) {
            logger.warn("Can't add instructor. Error message: {}, \nTrace:\n {}", e.getMessage(),
                    Arrays.deepToString(e.getStackTrace()));
            logger.debug("create() - finished with DAOException.");
            throw new DAOException("Unable to add instructor. Error message: " + e.getMessage());
        }

        logger.debug("create() - finished.");
        return lastInsertId;
    }

    /**
     * Update.
     * <p/>
     * Update instructor record in DB. To update record, send changed instance of {@link ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity}.
     * In case of success, <strong>true</strong> will returned, either method will return <strong>false</strong>.
     * In cases, when exceptions were produced, method will thrown {@link DAOException} and add warning
     * record into logger.
     *
     * @param dto Instance of CourseInstructorEntity object.
     * @return True if record was updated.
     * @throws DAOException             Will thrown if parent Exception was produced.
     * @throws IllegalArgumentException Will thrown if dto is null.
     */
    public boolean update(CourseInstructorEntity dto) throws DAOException, IllegalArgumentException {
        logger.debug("update() - started.");

        if (dto == null) {
            logger.warn("Null dto in update()");
            logger.debug("update() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Can't update with null valued Entity.");
        }

        boolean isUpdated = false;
        logger.debug("Updating instructor with values: {}", dto);
        try {
            em.merge(dto);
            isUpdated = true;
            logger.debug("Instructor was successful updated");
        } catch (Exception e) {
            logger.warn("Error! Can't update instructor. Message: {} \nTrace:\n {}", e.getMessage(),
                    Arrays.deepToString(e.getStackTrace()));
            logger.debug("update() - finished with DAOException.");
            throw new DAOException("Unable to update instructor. Error message: " + e.getMessage());
        }

        logger.debug("update() - finished.");
        return isUpdated;
    }

    /**
     * Delete.
     * <p/>
     * Delete instructor record from db. In case of success <strong>true</strong> will be returned.
     * In other cases -- <strong>false</strong>. If some parent methods produced exceptions,
     * {@link DAOException} will be thrown.
     *
     * @param dto Instance of CourseInstructorEntity object.
     * @return True if record successful removed.
     * @throws IllegalArgumentException Will thrown if dto is null.
     * @throws DAOException             Will thrown if parent Exception was produced.
     */
    public boolean delete(CourseInstructorEntity dto) throws IllegalArgumentException, DAOException {
        logger.debug("delete() - started.");
        if (dto == null) {
            logger.warn("Null dto in delete()");
            logger.debug("delete() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Can't delete with null valued Entity.");
        }

        boolean isDeleted = false;
        logger.debug("Deleting instructor with values: {}", dto);

        try {
            CourseInstructorEntity instructor = em.find(CourseInstructorEntity.class, dto.getId());
            em.remove(instructor);
            isDeleted = true;
            logger.debug("Instructor was successful removed from DB.");
        } catch (Exception e) {
            logger.warn("Error! Can't delete instructor. Message: {} \nTrace:\n {}", e.getMessage(),
                    Arrays.deepToString(e.getStackTrace()));
            logger.debug("delete() - finished with DAOException.");
            throw new DAOException("Unable to delete instructor. Error message: " + e.getMessage());
        }

        logger.debug("delete() - finished.");
        return isDeleted;
    }

    /**
     * Find.
     * <p/>
     * Find instructor according to its primary key, that must send in dto.
     * In case of success, retrieved information will be returned as {@link ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity}.
     * If instructor can't be found, method will returns null.
     * In case when for some reason find() method can't be called or it generated some exception,
     * {@link DAOException} will be thrown and new log record will be produced.
     *
     * @param dto Instance of CourseInstructorEntity object.
     * @return Instance of founded CourseInstructorEntity object or null.
     * @throws IllegalArgumentException Will thrown if dto is null.
     * @throws DAOException             Will thrown if parent Exception was produced.
     */
    public CourseInstructorEntity find(CourseInstructorEntity dto) throws IllegalArgumentException, DAOException {
        logger.debug("find() - started.");
        if (dto == null) {
            logger.warn("Null dto in find()");
            logger.debug("find() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Can't find with null valued Entity.");
        }
        CourseInstructorEntity instructor = null;
        logger.debug("Finding instructor with values: {}", dto);
        try {
            instructor = em.find(CourseInstructorEntity.class, dto.getId());
            logger.debug("Instructor was successful found.");
        } catch (Exception e) {
            logger.warn("Error! Can't find instructor. Message: {} \nTrace:\n {}", e.getMessage(),
                    Arrays.deepToString(e.getStackTrace()));
            logger.debug("find() - finished with DAOException.");
            throw new DAOException("Unable to find instructor. Error message: " + e.getMessage());

        }

        logger.debug("find() - finished.");
        return instructor;
    }

    /**
     * Find by Id.
     * <p/>
     * Find instructor by its ID. This method familiar to
     * {@link InstructorDAO#find(ua.edu.nuos.jeetraining2012.cms.entity.CourseInstructorEntity)}
     * except one, this one uses long id as an argument.
     *
     * @param id Long id of instructor.
     * @return Instance of founded CourseInstructorEntity object or null.
     * @throws IllegalArgumentException Will thrown if id is less or equals to zero.
     * @throws DAOException             Will thrown if parent Exception was produced.
     */
    public CourseInstructorEntity findById(long id) throws IllegalArgumentException, DAOException {
        logger.debug("findByUserId() - started.");
        if (id <= 0) {
            logger.warn("Null dto in find()");
            logger.debug("findByUserId() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Can't find with id less or equals to zero.");
        }
        CourseInstructorEntity instructor = null;
        logger.debug("Finding instructor with ID: {}", id);
        try {
            instructor = em.find(CourseInstructorEntity.class, id);
            logger.debug("Instructor was successful found.");
        } catch (Exception e) {
            logger.warn("Error! Can't find instructor. Message: {} \nTrace:\n {}", e.getMessage(),
                    Arrays.deepToString(e.getStackTrace()));
            logger.debug("findByUserId() - finished with DAOException.");
            throw new DAOException("Unable to find instructor. Error message: " + e.getMessage());
        }

        logger.debug("findByUserId() - finished.");
        return instructor;
    }
}
